<template>
  <div class="screen-share-container">
    <h1>Screen Recorder</h1>

    <div class="main-content">
      <!-- Left -->
      <div class="preview-section"> 
        <video ref="previewVideo"
               autoplay
               muted
               playsinline
               :class="{ active: isSharing }"></video>
        <div class="stats-display">
          <div class="stat-item">
            <span class="stat-label">Counter:</span>
            <span class="stat-value">{{ realTime }}</span>
          </div>
          <div class="stat-item">
            <span class="stat-label">Duretion:</span>
            <span class="stat-value">{{ formattedDuration }}</span>
          </div>
          <div class="stat-item">
            <span class="stat-label">FPS:</span>
            <span class="stat-value">{{ currentFPS.toFixed(1) }} FPS</span>
          </div>
          <div class="stat-item">
            <span class="stat-label">File Size:</span>
            <span class="stat-value">{{ formatFileSize(dataSize) }}</span>
          </div>
          <div v-if="segments.length > 0" class="stat-item">
            <span class="stat-label">Segment Count:</span>
            <span class="stat-value">{{ segments.length }}</span>
          </div>
        </div>
      </div>

      <!-- Right -->
      <div class="control-section">
        <div class="settings-section"> 
          <div class="settings-grid">
            <div class="setting-item">
              <label>Audio BitsPerSecond</label>
              <input type="number" v-model.number="recorderOptions.audioBitsPerSecond">
            </div>
            <div class="setting-item">
              <label>Video BitsPerSecond</label>
              <input type="number" v-model.number="recorderOptions.videoBitsPerSecond">
            </div>
            <div class="setting-item">
              <label for="mimeTypeInput">Mdeia Type[video/mp4|video/webm|video/x-matroska]</label> 
              <input id="mimeTypeInput" 
                     v-model="recorderOptions.mimeType"  > 
            </div>
            <div class="setting-item">
              <label>Data Collection Interval (ms)</label>
              <input type="number" v-model.number="dataCollectionInterval" min="100" max="5000">
            </div>
          </div>
        </div>

        <div class="recording-section"> 
          <div class="recording-controls">
            <button @click="selectScreen">
              Select Screen
            </button>
            <button @click="startRecordingWithFile">
              Start Recording
            </button>
            <button @click="pauseRecording">
              Pause Recording
            </button>
            <button @click="resumeRecording">
              Resume Recording
            </button>
            <button @click="stopRecording">
              Stop Recording
            </button>
            <button @click="addSegmentMarker" :disabled="!isRecording">
              Add Segment Marker
            </button>
          </div>
          <div class="recording-status">
            <span v-if="isRecording" :class="{ paused: isPaused }">
              {{ isPaused ? 'Paused' : 'Recording...' }}
              ({{ actualBitrate ? formatBitrate(actualBitrate) : 'Calc...' }})
            </span>
            <span v-else>No Record</span>
          </div>
        </div>

        <div class="file-info" v-if="currentFile">
          <h3>File</h3>
          <p>{{ currentFile.name }}</p>
          <div class="segments-list" v-if="segments.length > 0">
            <h4>Segments</h4>
            <ul>
              <li v-for="(segment, index) in segments" :key="index">
                Segment {{ index + 1 }}: {{ formatDuration(segment.duration) }}
                <span v-if="segment.note">({{ segment.note }})</span>
              </li>
            </ul>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { ref, watch, onMounted, onUnmounted, computed } from 'vue';

  const previewVideo = ref<HTMLVideoElement | null>(null); 
  const currentFile = ref<FileSystemFileHandle | null>(null);
   
  const realTimeInterval = ref<NodeJS.Timeout | null>(null);
  const realTime = ref<string>("00:00:00.000");
  const elapsedTime = ref<number>(0);
  const timerStartTime = ref<number | null>(null);
  const timerPausedTime = ref<number | null>(null);
  const totalTimerPausedDuration = ref<number>(0);
  const recordingStartTime = ref<number | null>(null);
  const recordingPausedTime = ref<number | null>(null);
  const totalPausedDuration = ref<number>(0);
  const lastFrameTime = ref<number | null>(null);
  const frameCount = ref<number>(0);
  const currentFPS = ref<number>(0);
  const dataSize = ref<number>(0);
  const lastDataSize = ref<number>(0);
  const lastBitrateCalculationTime = ref<number>(0);
  const actualBitrate = ref<number>(0);
  const segments = ref<Array<{ startTime: number, duration: number, note?: string }>>([]);
  const currentSegmentStart = ref<number | null>(null);
  const dataCollectionInterval = ref<number>(1000); 
 
  const streamOptions = ref<DisplayMediaStreamOptions>({
    video: true,
    audio: true,
  });
  const mediaStream = ref<MediaStream | null>(null);
  const recorderOptions = ref<MediaRecorderOptions>({
    mimeType: 'video/mp4;codecs="avc1.640028,mp4a.40.2"',
    audioBitsPerSecond: 128_000,
    videoBitsPerSecond: 25_000_000,
  });
  const fileStream = ref<FileSystemWritableFileStream | null>(null);
  const isRecording = ref(false);
  const isSharing = ref(false);
  const isPaused = ref(false);
  const recorder = ref<MediaRecorder | null>(null);
   
  const formattedDuration = computed(() => {
    return formatDuration(currentDuration.value);
  });

  const currentDuration = computed(() => {
    if (!recordingStartTime.value) return 0;

    const now = isRecording.value ? performance.now() : recordingPausedTime.value || performance.now();
    let duration = now - recordingStartTime.value - totalPausedDuration.value;
 
    return Math.max(0, duration);
  });
 
  const formatDuration = (ms: number) => {
    const date = new Date(ms);
    return date.toISOString().substr(11, 8) + '.' + Math.floor(ms % 1000).toString().padStart(3, '0');
  };

  const formatFileSize = (bytes: number) => {
    if (bytes < 1024) return bytes + ' B';
    else if (bytes < 1048576) return (bytes / 1024).toFixed(2) + ' KB';
    else if (bytes < 1073741824) return (bytes / 1048576).toFixed(2) + ' MB';
    else return (bytes / 1073741824).toFixed(2) + ' GB';
  };

  const formatBitrate = (bps: number) => {
    if (bps < 1000) return bps.toFixed(0) + ' bps';
    else if (bps < 1000000) return (bps / 1000).toFixed(1) + ' Kbps';
    else return (bps / 1000000).toFixed(1) + ' Mbps';
  };
 
  const updateFrameRate = () => {
    const now = performance.now();
    if (lastFrameTime.value) {
      const delta = now - lastFrameTime.value;
      frameCount.value++;
 
      if (delta >= 1000) {
        currentFPS.value = (frameCount.value * 1000) / delta;
        frameCount.value = 0;
        lastFrameTime.value = now;
      }
    } else {
      lastFrameTime.value = now;
    }
  };
 
  const updateBitrate = (newDataSize: number) => {
    const now = performance.now();
    if (lastBitrateCalculationTime.value > 0) {
      const timeElapsed = now - lastBitrateCalculationTime.value;
      const dataDelta = newDataSize - lastDataSize.value;
      actualBitrate.value = (dataDelta * 8) / (timeElapsed / 1000); // 转换为bps
    }
    lastDataSize.value = newDataSize;
    lastBitrateCalculationTime.value = now;
  };
   
  const addSegmentMarker = (note?: string) => {
    if (!currentSegmentStart.value) return;

    const now = performance.now();
    const segmentDuration = now - currentSegmentStart.value;

    segments.value.push({
      startTime: currentSegmentStart.value,
      duration: segmentDuration,
      note
    });

    currentSegmentStart.value = now;
  };
   
  const startTimer = () => {
    stopTimer();
    timerStartTime.value = performance.now();
    timerPausedTime.value = null;
    totalTimerPausedDuration.value = 0;

    realTimeInterval.value = setInterval(() => {
      if (timerStartTime.value) {
        const now = performance.now();
        let duration = now - timerStartTime.value - totalTimerPausedDuration.value;
        elapsedTime.value = duration;
        realTime.value = formatDuration(duration);
      }
    }, 10); 
  };

  const pauseTimer = () => {
    if (realTimeInterval.value && timerStartTime.value && !timerPausedTime.value) {
      timerPausedTime.value = performance.now();
      clearInterval(realTimeInterval.value);
      realTimeInterval.value = null;
    }
  };

  const resumeTimer = () => {
    if (timerPausedTime.value && timerStartTime.value) {
      totalTimerPausedDuration.value += performance.now() - timerPausedTime.value;
      timerPausedTime.value = null;
      startTimer();
    }
  };

  const stopTimer = () => {
    if (realTimeInterval.value) {
      clearInterval(realTimeInterval.value);
      realTimeInterval.value = null;
    }
    elapsedTime.value = 0;
    realTime.value = "00:00:00.000";
  };

  const startSharing = async (): Promise<void> => {
    try {
      await stopSharing();
      mediaStream.value = await navigator.mediaDevices.getDisplayMedia(streamOptions.value);
      mediaStream.value.getTracks().forEach((track) => {
        track.onended = () => {
          stopRecording();
          stopSharing();
        };
      }); 
      if (previewVideo.value) {
        previewVideo.value.srcObject = mediaStream.value;
        previewVideo.value.onplaying = () => {
          lastFrameTime.value = performance.now();
          frameCount.value = 0;
        };
        previewVideo.value.requestVideoFrameCallback(updateFrameRate);
      }

      isSharing.value = true;
    } catch (error) {
      console.error(error);
      throw error;
    }
  };
 
  const stopSharing = (): void => {
    if (mediaStream.value) {
      mediaStream.value.getTracks().forEach((track) => track.stop());
      mediaStream.value = null;
    }
    isSharing.value = false;
  };

 
  const setFileStream = async (fileHandle: FileSystemFileHandle): Promise<void> => {
    try {
      // Close existing file stream
      if (fileStream.value) {
        await fileStream.value.close();
      }

      // Create new file stream
      fileStream.value = await fileHandle.createWritable();
      dataSize.value = 0;
      lastDataSize.value = 0;
      lastBitrateCalculationTime.value = 0;
    } catch (error) {
      console.error("Error setting file stream:", error);
      fileStream.value = null;
      throw error;
    }
  };

 
  const closeFileStream = async (): Promise<void> => {
    try {
      if (fileStream.value) {
        await fileStream.value.close();
        fileStream.value = null;
      }
    } catch (error) {
      console.error("Error closing file stream:", error);
    }
  };
 
  const startRecording = async (fileHandle?: FileSystemFileHandle): Promise<void> => {
    try {
      // If new file handle provided, set new file stream
      if (fileHandle) {
        await setFileStream(fileHandle);
      }

      // Ensure we have a file stream available
      if (!fileStream.value) {
        throw new Error("No file stream available for recording");
      }

      // Stop existing recording if any
      stopRecording();

      // Ensure we have a media stream
      if (!mediaStream.value) {
        await startSharing();
        if (!mediaStream.value) throw new Error("No active media stream");
      }
       
      recordingStartTime.value = performance.now();
      recordingPausedTime.value = null;
      totalPausedDuration.value = 0;
      currentSegmentStart.value = performance.now();
      segments.value = [];

      // Create new MediaRecorder
      recorder.value = new MediaRecorder(mediaStream.value, recorderOptions.value);

      // Set up event handlers
      recorder.value.ondataavailable = async (ev: BlobEvent) => {
        try {
          if (fileStream.value && ev.data.size > 0) {
            await fileStream.value.write(ev.data);
            dataSize.value += ev.data.size;
            updateBitrate(dataSize.value);
          }
        } catch (error) {
          console.error(error);
        }
      };

      recorder.value.onstop = async () => {
        try { 
          if (currentSegmentStart.value) {
            addSegmentMarker("录制结束");
          }

          await closeFileStream();
        } finally {
          isRecording.value = false;
          isPaused.value = false;
        }
      };

      recorder.value.onerror = (event: ErrorEvent) => {
        console.error(event.error);
        stopRecording();
      };

      // Start recording with specified interval
      recorder.value.start(dataCollectionInterval.value);
      startTimer();
      isRecording.value = true;
      isPaused.value = false;
    } catch (error) {
      console.error(error);
      stopRecording();
      throw error;
    }
  };
 
  const pauseRecording = (): void => {
    if (recorder.value && recorder.value.state === "recording") {
      recorder.value.pause();
      pauseTimer();
      recordingPausedTime.value = performance.now();
      isPaused.value = true; 
      addSegmentMarker("pause");
    }
  };

 
  const resumeRecording = (): void => {
    if (recorder.value && recorder.value.state === "paused") {
      recorder.value.resume();
      resumeTimer();
      if (recordingPausedTime.value) {
        totalPausedDuration.value += performance.now() - recordingPausedTime.value;
      }
      recordingPausedTime.value = null;
      isPaused.value = false;
       
      currentSegmentStart.value = performance.now();
    }
  };
   
  const stopRecording = (): void => {
    if (recorder.value && recorder.value.state !== "inactive") {
      recorder.value.stop();
    }
    stopTimer();
    recorder.value = null;
    isRecording.value = false;
    isPaused.value = false;
  };
   
  const cleanup = async (): Promise<void> => {
    stopTimer();
    stopRecording();
    stopSharing();
    await closeFileStream();
  };
   
  const selectScreen = async () => {
    stopSharing();
    await startSharing();
  };
   
  const startRecordingWithFile = async () => {
    try {
      if (!('showSaveFilePicker' in window)) {
        alert('Please use new Chrome or Edge');
        return;
      }
      const fileHandle = await window.showSaveFilePicker({
        types: [
          {
            description: "MPEG files",  
            accept: { "video/mp4": [".mp4"] }, 
          },
          {
            description: "WEBM files",  
            accept: { "video/webm": [".webm"] },  
          },
          {
            description: "MKV files", 
            accept: { "video/x-matroska": [".mkv"] }, 
          },
          {
            description: "All files", 
            accept: {},  
          },
        ],
        suggestedName: `recording-${new Date().toISOString().replace(/[:.]/g, '-')}`
      });
      currentFile.value = fileHandle;
      await startRecording(fileHandle);
    } catch (error) {
      alert(error);
    }
  };

  watch(dataCollectionInterval, (newVal) => {
    if (recorder.value && recorder.value.state === "recording") {
      const wasPaused = isPaused.value;
      if (wasPaused) resumeRecording();

      recorder.value.requestData(); 
      recorder.value.stop(); 
      recorder.value.start(newVal); 

      if (wasPaused) pauseRecording();
    }
  });

  onMounted(() => {
    if (previewVideo.value) {
      const updateFrame = () => {
        updateFrameRate();
        previewVideo.value?.requestVideoFrameCallback(updateFrame);
      };
      previewVideo.value.requestVideoFrameCallback(updateFrame);
    }
  });

  onUnmounted(() => {
    cleanup();
  });
</script>

<style scoped>
  .screen-share-container {
    margin: 0 auto;
    padding: 20px;
    font-family: Arial, sans-serif;
  }

  h1, h2, h3, h4 {
    color: #333;
  }

  .main-content {
    display: flex;
    gap: 20px;
    margin-top: 20px;
  }

  .preview-section {
    flex: 2;
    display: flex;
    flex-direction: column;
    gap: 15px;
  }

  .control-section {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 20px;
  }

  video {
    width: 100%;
    height: auto;
    max-height: 500px;
    background-color: #000;
    border-radius: 8px;
    display: block;
  }

    video.active {
      border: 3px solid #4CAF50;
    }

  .settings-section {
    background-color: #f5f5f5;
    padding: 15px;
    border-radius: 8px;
  }

  .settings-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 15px;
  }

  .setting-item {
    display: flex;
    flex-direction: column;
    gap: 5px;
  }

    .setting-item label {
      font-weight: bold;
      margin-bottom: 5px;
    }

    .setting-item input[type="number"] {
      padding: 8px;
      border: 1px solid #ddd;
      border-radius: 4px;
      width: 100%;
    }

    .setting-item select {
      padding: 8px;
      border: 1px solid #ddd;
      border-radius: 4px;
      width: 100%;
    }

  .preview-controls, .recording-controls {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
  }

  button {
    padding: 10px 15px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-weight: bold;
    transition: background-color 0.3s;
    flex: 1 1 120px;
  }

    button:hover {
      background-color: #45a049;
    }

    button:disabled {
      background-color: #cccccc;
      cursor: not-allowed;
    }

  .recording-status {
    font-weight: bold;
    padding: 10px;
    border-radius: 4px;
    background-color: #f8f8f8;
    text-align: center;
    margin-top: 10px;
  }

    .recording-status span {
      color: #d32f2f;
    }

      .recording-status span.paused {
        color: #ff9800;
      }

      .recording-status span:first-child {
        color: #4CAF50;
      }

  .file-info {
    padding: 15px;
    background-color: #e8f5e9;
    border-radius: 8px;
  }

    .file-info p {
      margin: 5px 0 0;
      word-break: break-all;
    }

  .stats-display {
    background-color: #f0f0f0;
    padding: 10px;
    border-radius: 8px;
    margin-top: 10px;
  }

  .stat-item {
    display: flex;
    justify-content: space-between;
    margin-bottom: 5px;
  }

  .stat-label {
    font-weight: bold;
  }

  .stat-value {
    font-family: monospace;
  }

  .segments-list {
    margin-top: 10px;
  }

    .segments-list ul {
      list-style-type: none;
      padding-left: 0;
      margin-top: 5px;
    }

    .segments-list li {
      padding: 5px 0;
      border-bottom: 1px solid #ddd;
      font-size: 0.9em;
    }

  @media (max-width: 768px) {
    .main-content {
      flex-direction: column;
    }

    .preview-section, .control-section {
      flex: none;
      width: 100%;
    }
  }
</style>
